Conversation
|
[CI]: Can one of the admins verify this patch? |
| const IGPUGraphicsPipeline* getBoundGraphicsPipeline() const { return m_boundGraphicsPipeline; } | ||
| const IGPUPipelineBase* getBoundGraphicsPipeline() const { return m_boundRasterizationPipeline; } |
There was a problem hiding this comment.
can we have a RasterizationPipelineBase, cause PipelineBase is also inherited from by RT and compute
| void CVulkanLogicalDevice::createGraphicsPipelines_impl( | ||
| IGPUPipelineCache* const pipelineCache, | ||
| const std::span<const IGPUGraphicsPipeline::SCreationParams> createInfos, | ||
| core::smart_refctd_ptr<IGPUGraphicsPipeline>* const output, | ||
| const SSpecializationValidationResult& validation | ||
| ) | ||
| { | ||
| auto getVkStencilOpStateFrom = [](const asset::SStencilOpParams& params)->VkStencilOpState | ||
| { | ||
| return { | ||
| .failOp = static_cast<VkStencilOp>(params.failOp), | ||
| .passOp = static_cast<VkStencilOp>(params.passOp), | ||
| .depthFailOp = static_cast<VkStencilOp>(params.depthFailOp), | ||
| .compareOp = static_cast<VkCompareOp>(params.compareOp) | ||
| }; | ||
| void PopulateViewport(VkPipelineViewportStateCreateInfo& outViewport, nbl::asset::SRasterizationParams const& raster) { | ||
| outViewport.viewportCount = raster.viewportCount; | ||
| // must be identical to viewport count unless VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT or VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT are used | ||
| outViewport.scissorCount = raster.viewportCount; | ||
| } | ||
|
|
||
|
|
||
| void PopulateRaster(VkPipelineRasterizationStateCreateInfo& outRaster, nbl::asset::SRasterizationParams const& raster) { | ||
| outRaster.depthClampEnable = raster.depthClampEnable; | ||
| outRaster.rasterizerDiscardEnable = raster.rasterizerDiscard; | ||
| outRaster.polygonMode = static_cast<VkPolygonMode>(raster.polygonMode); | ||
| outRaster.cullMode = static_cast<VkCullModeFlags>(raster.faceCullingMode); | ||
| outRaster.frontFace = raster.frontFaceIsCCW ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE; | ||
| outRaster.depthBiasEnable = raster.depthBiasEnable; | ||
| } | ||
|
|
||
| void PopulateMultisample(VkPipelineMultisampleStateCreateInfo& outMultisample, nbl::asset::SRasterizationParams const& raster) { | ||
| outMultisample.rasterizationSamples = static_cast<VkSampleCountFlagBits>(0x1 << raster.samplesLog2); | ||
| if (raster.minSampleShadingUnorm > 0) { | ||
| outMultisample.sampleShadingEnable = true; | ||
| outMultisample.minSampleShading = float(raster.minSampleShadingUnorm) / 255.f; | ||
| } | ||
| else { | ||
| outMultisample.sampleShadingEnable = false; | ||
| outMultisample.minSampleShading = 0.f; | ||
| } | ||
| outMultisample.pSampleMask = raster.sampleMask; | ||
| outMultisample.alphaToCoverageEnable = raster.alphaToCoverageEnable; | ||
| outMultisample.alphaToOneEnable = raster.alphaToOneEnable; | ||
| } | ||
| VkStencilOpState getVkStencilOpStateFrom(const asset::SStencilOpParams& params) { | ||
| return { | ||
| .failOp = static_cast<VkStencilOp>(params.failOp), | ||
| .passOp = static_cast<VkStencilOp>(params.passOp), | ||
| .depthFailOp = static_cast<VkStencilOp>(params.depthFailOp), | ||
| .compareOp = static_cast<VkCompareOp>(params.compareOp) | ||
| }; | ||
| } | ||
|
|
||
| const auto& features = getEnabledFeatures(); | ||
| void PopulateDepthStencil(VkPipelineDepthStencilStateCreateInfo& outDepthStencil, nbl::asset::SRasterizationParams const& raster) { | ||
| outDepthStencil.depthTestEnable = raster.depthTestEnable(); | ||
| outDepthStencil.depthWriteEnable = raster.depthWriteEnable; | ||
| outDepthStencil.depthCompareOp = static_cast<VkCompareOp>(raster.depthCompareOp); | ||
| outDepthStencil.depthBoundsTestEnable = raster.depthBoundsTestEnable; | ||
| outDepthStencil.stencilTestEnable = raster.stencilTestEnable(); | ||
| outDepthStencil.front = getVkStencilOpStateFrom(raster.frontStencilOps); | ||
| outDepthStencil.back = getVkStencilOpStateFrom(raster.backStencilOps); | ||
| } | ||
|
|
||
| void PopulateColorBlend( | ||
| VkPipelineColorBlendStateCreateInfo& outColorBlend, | ||
| VkPipelineColorBlendAttachmentState*& outColorBlendAttachmentState, | ||
| nbl::asset::SBlendParams const& blend, | ||
| nbl::asset::IRenderpass::SCreationParams::SSubpassDescription const& subpass | ||
| ) { | ||
| //outColorBlend->flags no attachment order access yet | ||
| outColorBlend.logicOpEnable = blend.logicOp != asset::ELO_NO_OP; | ||
| outColorBlend.logicOp = getVkLogicOpFromLogicOp(blend.logicOp); | ||
| outColorBlend.pAttachments = outColorBlendAttachmentState; | ||
| for (auto i = 0; i < IGPURenderpass::SCreationParams::SSubpassDescription::MaxColorAttachments; i++) { | ||
| if (subpass.colorAttachments[i].render.used()) { | ||
| const auto& params = blend.blendParams[i]; | ||
| outColorBlendAttachmentState->blendEnable = params.blendEnabled(); | ||
| outColorBlendAttachmentState->srcColorBlendFactor = getVkBlendFactorFromBlendFactor(static_cast<asset::E_BLEND_FACTOR>(params.srcColorFactor)); | ||
| outColorBlendAttachmentState->dstColorBlendFactor = getVkBlendFactorFromBlendFactor(static_cast<asset::E_BLEND_FACTOR>(params.dstColorFactor)); | ||
| outColorBlendAttachmentState->colorBlendOp = getVkBlendOpFromBlendOp(static_cast<asset::E_BLEND_OP>(params.colorBlendOp)); | ||
| outColorBlendAttachmentState->srcAlphaBlendFactor = getVkBlendFactorFromBlendFactor(static_cast<asset::E_BLEND_FACTOR>(params.srcAlphaFactor)); | ||
| outColorBlendAttachmentState->dstAlphaBlendFactor = getVkBlendFactorFromBlendFactor(static_cast<asset::E_BLEND_FACTOR>(params.dstAlphaFactor)); | ||
| outColorBlendAttachmentState->alphaBlendOp = getVkBlendOpFromBlendOp(static_cast<asset::E_BLEND_OP>(params.alphaBlendOp)); | ||
| outColorBlendAttachmentState->colorWriteMask = getVkColorComponentFlagsFromColorWriteMask(params.colorWriteMask); | ||
| outColorBlendAttachmentState++; | ||
| //^that pointer iterator is how we ensure the attachments or consecutive | ||
| } | ||
| } | ||
| outColorBlend.attachmentCount = std::distance<const VkPipelineColorBlendAttachmentState*>(outColorBlend.pAttachments, outColorBlendAttachmentState); | ||
| } | ||
|
|
||
| template<typename SCreationParams> | ||
| void PopulateMeshGraphicsCommonData( | ||
| const std::span<const SCreationParams> createInfos, | ||
| core::vector<VkGraphicsPipelineCreateInfo>& vk_createInfos, | ||
|
|
||
| core::vector<VkPipelineViewportStateCreateInfo>& vk_viewportStates, | ||
| core::vector<VkPipelineRasterizationStateCreateInfo>& vk_rasterizationStates, | ||
| core::vector<VkPipelineMultisampleStateCreateInfo>& vk_multisampleStates, | ||
| core::vector<VkPipelineDepthStencilStateCreateInfo>& vk_depthStencilStates, | ||
| core::vector<VkPipelineColorBlendStateCreateInfo>& vk_colorBlendStates, | ||
| core::vector<VkPipelineColorBlendAttachmentState>& vk_colorBlendAttachmentStates, | ||
|
|
||
| core::vector<VkDynamicState>& vk_dynamicStates, | ||
| const VkPipelineDynamicStateCreateInfo& vk_dynamicStateCreateInfo | ||
| ) { | ||
| //the main concern is lifetime, so don't want to construct, move, or copy anything in here | ||
|
|
||
| auto outColorBlendAttachmentState = vk_colorBlendAttachmentStates.data(); //the pointer iterator is used | ||
|
|
||
| core::vector<VkDynamicState> vk_dynamicStates = { | ||
|
|
||
| for (uint32_t i = 0; i < createInfos.size(); i++) { //whats the maximum number of pipelines that can be created at once? uint32_t to be safe | ||
| auto& info = createInfos[i]; | ||
| const auto& blend = info.cached.blend; | ||
| const auto& raster = info.cached.rasterization; | ||
| const auto& subpass = info.renderpass->getCreationParameters().subpasses[info.cached.subpassIx]; | ||
|
|
||
| initPipelineCreateInfo(&vk_createInfos[i], info); | ||
|
|
||
| PopulateViewport(vk_viewportStates[i], raster); | ||
| PopulateRaster(vk_rasterizationStates[i], raster); | ||
| PopulateMultisample(vk_multisampleStates[i], raster); | ||
| PopulateDepthStencil(vk_depthStencilStates[i], raster); | ||
| PopulateColorBlend(vk_colorBlendStates[i], outColorBlendAttachmentState, blend, subpass); | ||
| //PopulateDynamicState(dynState, ?) | ||
|
|
||
|
|
||
| vk_createInfos[i].pViewportState = &vk_viewportStates[i]; | ||
| vk_createInfos[i].pRasterizationState = &vk_rasterizationStates[i]; | ||
| vk_createInfos[i].pMultisampleState = &vk_multisampleStates[i]; | ||
| vk_createInfos[i].pDepthStencilState = &vk_depthStencilStates[i]; | ||
| vk_createInfos[i].pColorBlendState = &vk_colorBlendStates[i]; | ||
| vk_createInfos[i].pDynamicState = &vk_dynamicStateCreateInfo; | ||
| vk_createInfos[i].renderPass = static_cast<const CVulkanRenderpass*>(info.renderpass)->getInternalObject(); | ||
| vk_createInfos[i].subpass = info.cached.subpassIx; | ||
| //handle | ||
| //index | ||
| //layout? | ||
| // ^ handled in initPipelineCreateInfo | ||
| } | ||
| } | ||
|
|
||
| core::vector<VkDynamicState> getDefaultDynamicStates(SPhysicalDeviceFeatures const& features) { | ||
| core::vector<VkDynamicState> ret = { |
There was a problem hiding this comment.
@kevyuu you review this and give it the okay
There was a problem hiding this comment.
actually wait a ssecond, @GDBobby I think you could turn all these free floating functions into struct methods of a struct that has reference members like core::vector<>& or span members const std::span<> since the vectors are preallocated
| //potentially collapse this so Mesh just uses CVulkanGraphicsPipeline | ||
| //if thats done, BindMesh can go away | ||
| class CVulkanMeshPipeline final : public IGPUMeshPipeline |
There was a problem hiding this comment.
you still need correct inheritance unfortunately, you could template the class though
template<typename Base> requires std::is_base_of_v<IGPURasterizationPipeline,Base>
class CVulkanRasterizationPipeline : public Base| meshShaderFeatures.primitiveFragmentShadingRateMeshShader = VK_FALSE;//needs to be explicitly set? | ||
| meshShaderFeatures.meshShaderQueries = VK_FALSE; | ||
| meshShaderFeatures.multiviewMeshShader = VK_FALSE; |
There was a problem hiding this comment.
the question is, for regular Multiview, queries and Shading rate, do we currently have them as limits or features?
| //check if features are existant first | ||
| //potentially put a copy of VkPhysicalDeviceMeshShaderFeaturesEXT directly into features | ||
| //depends on the less obvious properties | ||
| if (isExtensionSupported(VK_EXT_MESH_SHADER_EXTENSION_NAME)) { | ||
| features.meshShader = meshShaderFeatures.meshShader; | ||
| features.taskShader = meshShaderFeatures.taskShader; | ||
| //TODO | ||
| //VkBool32 multiviewMeshShader; | ||
| //VkBool32 primitiveFragmentShadingRateMeshShader; | ||
| //VkBool32 meshShaderQueries; | ||
|
|
||
| //VkPhysicalDeviceMeshShaderPropertiesEXT | ||
| //#define LIMIT_INIT_MESH(limitMemberName) properties.limits.limitMemberName = meshShaderProperties.limitMemberName | ||
| //LIMIT_INIT_MESH(maxTaskWorkGroupTotalCount); | ||
| //LIMIT_INIT_MESH(maxTaskWorkGroupInvocations); | ||
| //LIMIT_INIT_MESH(maxTaskPayloadSize); | ||
| //LIMIT_INIT_MESH(maxTaskSharedMemorySize); | ||
| //LIMIT_INIT_MESH(maxTaskPayloadAndSharedMemorySize); | ||
| //LIMIT_INIT_MESH(maxMeshWorkGroupInvocations); | ||
| //LIMIT_INIT_MESH(maxMeshSharedMemorySize); | ||
| //LIMIT_INIT_MESH(maxMeshPayloadAndSharedMemorySize); | ||
| //LIMIT_INIT_MESH(maxMeshOutputMemorySize); | ||
| //LIMIT_INIT_MESH(maxMeshOutputComponents); | ||
| //LIMIT_INIT_MESH(maxMeshOutputVertices); | ||
| //LIMIT_INIT_MESH(maxMeshOutputPrimitives); | ||
| //LIMIT_INIT_MESH(maxMeshOutputLayers); | ||
| //LIMIT_INIT_MESH(maxMeshMultiviewViewCount); | ||
| //LIMIT_INIT_MESH(maxMeshOutputPerVertexGranularity); | ||
| //LIMIT_INIT_MESH(maxMeshOutputPerPrimitiveGranularity); | ||
|
|
||
| //for(uint8_t i = 0; i < 3; i++){ | ||
| // LIMIT_INIT_MESH(maxTaskWorkGroupCount[i]); | ||
| // LIMIT_INIT_MESH(maxTaskWorkGroupSize[i]); | ||
| // LIMIT_INIT_MESH(maxMeshWorkGroupCount[i]); | ||
| // LIMIT_INIT_MESH(maxMeshWorkGroupSize[i]); | ||
| //} | ||
| //#undef LIMIT_INIT_MESH | ||
| } |
There was a problem hiding this comment.
@Erfan-Ahmadi review and advise on how to expose
| inline const SCachedCreationParams& getCachedCreationParams() const | ||
| { | ||
| return pipeline_base_t::getCachedCreationParams(); | ||
| } | ||
|
|
||
| inline SCachedCreationParams& getCachedCreationParams() | ||
| { | ||
| assert(isMutable()); | ||
| return m_params; | ||
| } |
There was a problem hiding this comment.
if the creation params are identical for Gfx and MEsh, should go in ICPURasterizationPipeline
There was a problem hiding this comment.
99168b9#diff-45681900933e70545ee1a41c9ad2419f9cd40ef479ad7afe82dc97f29095c36aR13.
If Programmable Vertex Pulling is enforced, VertexParams could be removed from GraphicsPipeline. With preprocessor branching, primitive type could potentially be moved out the shader and into the creation params, allowng the creation params to be the same for both rasterization pipelines.
There was a problem hiding this comment.
I take that back, I only thought about outputtopology. Preprocessor branching the rest of the shader per primitive type is unreasonable.
There was a problem hiding this comment.
you can use composition ot inheritance to split the SCachedCreationParams.
we will support vertex inputs as long as we support graphics pipelines.
| namespace nbl::asset | ||
| { | ||
|
|
||
| class ICPUMeshPipeline final : public ICPUPipeline<IMeshPipeline<ICPUPipelineLayout,ICPURenderpass>> |
There was a problem hiding this comment.
make a ICPURasterizationPipeline : IRasterizationPipeline<ICPUPipelineLayout,ICPUrenderpass>
There was a problem hiding this comment.
I tried IRasterization earlier.
I took another look. I don't use polymorphism too much, so maybe there's an easy answer staring me in the face. Most of the issue is that IGPURasterization is an incomplete type unless it's specialized with IMesh or IGraphics, at which point it's not an effective abstraction.

Here is perhaps the simplest solution.

Another potential solution I have is removing IGraphics and IMesh and replacing with IRasterization. However, that introduces a new problem, the construction parameters would be above the IGPU/ICPU abstraction level, unless some virtual functions are used.

If none of these solutions are acceptable, my recommendation would be a larger rewrite of the Pipeline system as whole. I have ideas here, but I'll hold until further direction is given.
include/nbl/video/IGPUMeshPipeline.h
Outdated
| https://registry.khronos.org/vulkan/specs/latest/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-renderPass-07064 | ||
| * If renderPass is not VK_NULL_HANDLE, the pipeline is being created with pre-rasterization shader state, subpass viewMask is not 0, and multiviewMeshShader is not enabled, then pStages must not include a mesh shader |
There was a problem hiding this comment.
you can check that
include/nbl/video/IGPUMeshPipeline.h
Outdated
| https://registry.khronos.org/vulkan/specs/latest/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07066 | ||
| * If the pipeline requires pre-rasterization shader state, and includes a mesh shader, there must be no element of the | ||
| * pDynamicStates member of pDynamicState set to VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE, or VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT | ||
|
|
There was a problem hiding this comment.
we choose the dynamic state ourselves (opinionated pick), and I think we never made those two dynamic, ever
maybe check the PRIMITIVE_RESTART enable
include/nbl/video/IGPUMeshPipeline.h
Outdated
| https://registry.khronos.org/vulkan/specs/latest/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-renderPass-07720 | ||
| * If renderPass is VK_NULL_HANDLE, the pipeline is being created with pre-rasterization shader state, and | ||
| * VkPipelineRenderingCreateInfo::viewMask is not 0, and multiviewMeshShader is not enabled, then pStages must not include a mesh shader | ||
|
|
There was a problem hiding this comment.
what its saying is that you can't use multivew in pipeline create info with a mesh shader if you don't have the multiviewMeshShader feature
Basically there's the old multivew feature, but that only controls if multiview is available for a graphics pipeline, for mesh you have another thing to check instead
| if (!processSpecInfo(fragmentShader, hlsl::ShaderStage::ESS_FRAGMENT)) return {}; | ||
|
|
||
| if (!hasRequiredStages(stagePresence)) | ||
| return {}; |
There was a problem hiding this comment.
because you haven't committed the IMeshPipeline.h file I can't see the impl of this
there will be another commit. I renamed meshgraphicscommon to rasterizationpipelinecommon i added mesh pipelines to cassetconverter
Mesh Pipelines
Description of changes that aren't implicit.
Testing
https://github.com/GDBobby/Nabla-Examples-and-Tests/tree/master/MeshShader